`timescale 1ns/100ps

`include "sim_glb.sv"

module tc;

localparam          CLK_PRD                 = 5;
localparam          AW                      = 10;
localparam          BW                      = 12;
localparam          PW                      = AW+BW+1;

reg                                         rst_n;
reg                                         clk;


initial begin:CRG
    rst_n=1'b0;
    clk=1'b0;

    fork
        rst_n=#100.5 1'b1;
        forever clk=#CLK_PRD ~clk;
    join
end

RGRS_MNG    rgrs;
initial begin:REGRESS
    rgrs = new("tc_arith", 1);

    rgrs.wait_chks_done(100_000_000);
end

reg         signed  [16-1:0]                sid;
wire        signed  [8-1:0]                 sod;

reg         signed  [AW-1:0]                ar;     // real part of a
reg         signed  [AW-1:0]                ai;     // imaginary part of a
reg         signed  [BW-1:0]                br;     // real part of b
reg         signed  [BW-1:0]                bi;     // imaginary part of b

wire        signed  [PW-1:0]                pr;     // real part of p
wire        signed  [PW-1:0]                pi;     // imaginary part of p

reg                                         cur_vld;
reg                 [16-1:0]                cur_data;
wire                [16-1:0]                avg_data;

initial begin:GEN_SAT_TRU
    integer         i;
    reg [7:0]       rand_dat;

    i   = 0;
    sid = 16'h0;
    ar = 0;
    ai = 0;
    br = 0;
    bi = 0;

    @(posedge rst_n);

    @(posedge clk);
    sid =`U_DLY 16'h1234;

    @(posedge clk);
    sid =`U_DLY 16'h0234;

    @(posedge clk);
    sid =`U_DLY 16'h8234;
    
    @(posedge clk);
    sid =`U_DLY 16'hf234;

    @(posedge clk);
    sid =`U_DLY 16'hfa34;

    @(posedge clk);
    sid =`U_DLY 16'h0fff;

    @(posedge clk);
    sid =`U_DLY 16'h07ff;

    @(posedge clk);
    sid =`U_DLY 16'h07ef;

    @(posedge clk);
    sid =`U_DLY 16'hf0ef;

    @(posedge clk);
    sid =`U_DLY 16'hf8ef;

    @(posedge clk);
    sid =`U_DLY 16'hf7ff;

    @(posedge clk);
    sid =`U_DLY 16'hf800;

    @(posedge clk);
    sid =`U_DLY 16'h0fe1;

    @(posedge clk);
    sid =`U_DLY 16'h07f8;

    @(posedge clk);
    sid =`U_DLY 16'h07e8;

    @(posedge clk);
    sid =`U_DLY 16'h07e7;

    @(posedge clk);
    sid =`U_DLY 16'hf7f7;

    @(posedge clk);
    sid =`U_DLY 16'hf7f8;

    @(posedge clk);
    sid =`U_DLY 16'hf808;

    @(posedge clk);
    sid =`U_DLY 16'hfff8;

    @(posedge clk);
    sid =`U_DLY 16'hfff7;

    repeat(1000) begin
        @(posedge clk);
        {ar, ai} =`U_DLY $urandom();
        {br, bi} =`U_DLY $urandom();
    end

    #100.00;
    rgrs.one_chk_done("gen num is done.");
end

s_sat_tru #(
        .IDW                            (16                             ),
        .IFW                            (8                              ),
        .ODW                            (8                              ),
        .OFW                            (4                              ),
//        .TRU_MODE                       ("CBB_DEFINE"                   )	// default truncation mode follows cbb_define.v 
//        .TRU_MODE                       ("FLOOR"                        )	// discade fractional bits directly for less area and higher Fmax
        .TRU_MODE                       ("ROUND"                        )	// discade or carry according to MSB of fractonal bits for better DC
) u_s ( 
        .id                             (sid                            ),
        .od                             (sod                            ),
        .over                           (                               )
);

u_sat_tru #(
        .IDW                            (16                             ),	// input data width
        .IFW                            (8                              ),	// input fractional width
        .ODW                            (8                              ),	// output data width
        .OFW                            (4                              ),	// output fractional width
//        .TRU_MODE                       ("CBB_DEFINE"                   )	// default truncation mode follows cbb_define.v 
        .TRU_MODE                       ("FLOOR"                        )	// discade fractional bits directly for less area and higher Fmax
//        .TRU_MODE                       ("ROUND"                        )	// discade or carry according to MSB of fractonal bits for better DC
) u_u ( 
        .id                             (sid                            ),
        .od                             (                               ),
        .over                           (                               )
);

//s_sat_tru #(
//        .IDW                            (16                             ),
//        .IFW                            (8                              ),
//        .ODW                            (16                             ),
//        .OFW                            (8                              )
//) u_s ( 
//        .id                             (sid                            ),
//        .od                             (                               ),
//        .over                           (                               )
//);
//
//u_sat_tru #(
//        .IDW                            (16                             ),	// input data width
//        .IFW                            (8                              ),	// input fractional width
//        .ODW                            (16                             ),	// output data width
//        .OFW                            (8                              )	// output fractional width
//) u_u ( 
//        .id                             (sid                            ),
//        .od                             (                               ),
//        .over                           (                               )
//);
reg         signed  [AW+BW+1-1:0]           pr_v;
reg         signed  [AW+BW+1-1:0]           pi_v;

always@(posedge clk or negedge rst_n) begin
    if (rst_n==1'b0) begin
        pr_v <=`U_DLY {AW+BW+1{1'b0}};
        pi_v <=`U_DLY {AW+BW+1{1'b0}};
    end else begin
        pr_v <=`U_DLY ar*br-ai*bi;
        pi_v <=`U_DLY ai*br+bi*ar;

        if ((pr_v!=pr) || (pi_v!=pi)) begin
            $error("complex_mul result is wrong");
            $stop;
        end
    end
end

initial begin
    cur_vld  = 1'b0;
    cur_data = 16'h0;
    
    @(posedge rst_n);
    @(posedge clk);

    cur_vld  = `U_DLY 1'b1;
    cur_data = `U_DLY 16'h7fff;
    
    forever begin
        @(posedge clk);
        cur_data = `U_DLY $urandom();
    end
end

complex_mul #(
        .AW                             (AW                             ),
        .BW                             (BW                             )
) u_comp_mul ( 
        .rst_n                          (rst_n                          ),
        .clk                            (clk                            ),
        .cke                            (1'b1                           ),

        .ar                             (ar                             ),	// real part of a
        .ai                             (ai                             ),	// imaginary part of a
        .br                             (br                             ),	// real part of b
        .bi                             (bi                             ),	// imaginary part of b

        .pr                             (pr                             ),	// real part of p
        .pi                             (pi                             )	// imaginary part of p
);

s_ema #(     // Exponential Moving Average
        .DW                             (16                             ),	// data width
        .K_MAX                          (8                              ),	// Maximum of cfg_k, K_MAX>=1
        .K_MIN                          (0                              ) 	// Minimum of cfg_k,
) u_ema ( 
        .rst_n                          (rst_n                          ),
        .clk                            (clk                            ),
        .cke                            (1'b1                           ),

        .cur_vld                        (cur_vld                        ),
        .cur_data                       (cur_data                       ),
        .avg_data                       (avg_data                       ),

        .cfg_clr                        (1'b0                           ),
        .cfg_ini                        (16'hff12                       ),
        .cfg_k                          (4'd8                           )	// range [0:KM], alpha /2^k
);

s_msum #(     // moving average
        .DW                             (16                             ),	// data width
        .N_MAX                          (4                              ) 	// N range[3, N_MAX]
) u_s_msum ( 
        .rst_n                          (rst_n                          ),
        .clk                            (clk                            ),
        .cke                            (1'b1                           ),

        .cur_vld                        (cur_vld                        ),
        .cur_data                       (cur_data                       ),
        .sum_data                       (                               ),	// avg_data /N)

        .cfg_clr                        (1'b0                           ),
        .cfg_ini                        (18'b0                          ),
        .cfg_dly                        (2'd1                           )	// cfg_dly = N-3
);

u_ema #(     // Exponential Moving Average
        .DW                             (16                             ),	// data width
        .K_MAX                          (8                              ),	// Maximum of cfg_k, K_MAX>=1
        .K_MIN                          (0                              ) 	// Minimum of cfg_k,
) u_u_ema ( 
        .rst_n                          (rst_n                          ),
        .clk                            (clk                            ),
        .cke                            (1'b1                           ),

        .cur_vld                        (cur_vld                        ),
        .cur_data                       (cur_data                       ),
        .avg_data                       (                               ),

        .cfg_clr                        (1'b0                           ),
        .cfg_ini                        (16'hff12                       ),
        .cfg_k                          (4'd8                           )	// range [0:KM], alpha /2^k
);

u_msum #(
        .DW                             (16                             ),	// data width
        .N_MAX                          (4                              ) 	// N range[3, N_MAX]
) u_u_msum ( 
        .rst_n                          (rst_n                          ),
        .clk                            (clk                            ),
        .cke                            (1'b1                           ),

        .cur_vld                        (cur_vld                        ),
        .cur_data                       (cur_data                       ),
        .sum_data                       (                               ),	// avg_data /N)

        .cfg_clr                        (1'b0                           ),
        .cfg_ini                        (18'd0                          ),
        .cfg_dly                        (2'd1                           )	// cfg_dly
);

endmodule

